package com.czk.bootlettuce.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 在工作当中，我们经常会遇到与统计相关的功能需求，比如统计网站PV（PageView页面访问量）,可以使用Redis的incr、incrby轻松实现。
 * 但像UV（UniqueVisitor，独立访客）、独立IP数、搜索记录数等需要去重和计数的问题如何解决？这种求集合中不重复元素个数的问题称为基数问题。
 * 解决基数问题有很多种方案：
 * （1）数据存储在MySQL表中，使用distinct count计算不重复个数
 * （2）使用Redis提供的hash、set、bitmaps等数据结构来处理
 * 以上的方案结果精确，但随着数据不断增加，导致占用空间越来越大，对于非常大的数据集是不切实际的。
 * 能否能够降低一定的精度来平衡存储空间？Redis推出了HyperLogLog
 * Redis HyperLogLog 是用来做基数统计的算法，HyperLogLog 的优点是，在输入元素的数量或者体积非常非常大时，计算基数所需的空间总是固定的、并且是很小的。
 * 在 Redis 里面，每个 HyperLogLog 键只需要花费 12 KB 内存，就可以计算接近 2^64 个不同元素的基数。这和计算基数时，元素越多耗费内存就越多的集合形成鲜明对比。
 * 但是，因为 HyperLogLog 只会根据输入元素来计算基数，而不会储存输入元素本身，所以 HyperLogLog 不能像集合那样，返回输入的各个元素。
 * <p>
 * 什么是基数?
 * 比如数据集 {1, 3, 5, 7, 5, 7, 8}， 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内，快速计算基数。
 *
 * @Author:ChenZhangKun
 * @Date: 2021/12/5 15:47
 */
@RestController
@RequestMapping("/hyper")
public class HyperLogLogController {
    private static final String KEY_1 = "hll1";
    private static final String KEY_2 = "hll2";
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 添加
     *
     * @param value
     * @return
     */
    @GetMapping("/add/{key}/{value}")
    public String add(@PathVariable String key, @PathVariable String value) {
        Long add = redisTemplate.opsForHyperLogLog().add(key, value);
        return add == 1 ? "已添加" : "已存在";
    }

    /**
     * 长度
     *
     * @param key
     * @return
     */
    @GetMapping("/count/{key}")
    public Long count(@PathVariable String key) {
        Long size = redisTemplate.opsForHyperLogLog().size(key);
        return size;
    }

    @GetMapping("/merge/{newKey}/{key1}/{key2}")
    public Long merge(@PathVariable String newKey, @PathVariable String key1, @PathVariable String key2) {
        Long union = redisTemplate.opsForHyperLogLog().union(newKey, key2, key1);
        return union;
    }
}
